<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script>
            case0();
            case1();
            case2();
            case3();
            case4();
            case5();
            case6();

            function case0() {
                (function () {
                    // 1. 最基本的，原型链的prototype指向原型对象，并且就是constructor
                    function Father() {
                        this.param = "father";
                    }
                    var instance1 = new Father();
                    // console.log(instance1);
                    console.log(instance1.__proto__.constructor === Father); // 也等于 Father.prototype.constructor
                    console.log("--------------------------------------------");
                })();
            }

            function case1() {
                (function () {
                    // 1. 原型链继承，最基础的继承
                    function Father() {
                        this.param = "father";
                        this._father = "F";
                    }
                    function Son() {
                        this.param = "son";
                        this._son = "S";
                    }
                    Son.prototype = new Father();
                    var instance1 = new Son();
                    console.log(instance1);
                    console.log("of father:", instance1 instanceof Father);
                    console.log("of son:", instance1 instanceof Son);
                    console.log("--------------------------------------------");
                })();
            }

            function case2() {
                (function () {
                    // 2. 经典继承，借用构造函数
                    function Father() {
                        this.colors = ["blue", "red", "greed"];
                    }
                    function Son() {
                        Father.call(this);
                    }
                    var instance1 = new Son();
                    instance1.colors.push("black");
                    console.log(instance1);
                    console.log("of father:", instance1 instanceof Father);
                    console.log("of son:", instance1 instanceof Son);

                    var instance2 = new Son();
                    console.log(instance2);
                    console.log("--------------------------------------------");
                })();
            }

            function case3() {
                (function () {
                    // 3. 组合继承，又叫伪经典继承。
                    function Father(name) {
                        this.name = name;
                        this.colors = ["red", "blue", "green"];
                    }
                    Father.prototype.sayName = function () {
                        console.log("my name is ", this.name);
                    };
                    function Son(name, age) {
                        Father.call(this, name); // 继承实例属性，第一次调用Father()
                        this.age = age;
                    }
                    Son.prototype = new Father(); // 继承父亲方法，第二次调用Father()
                    Son.prototype.sayAge = function () {
                        console.log("My age is ", this.age);
                    };

                    var instance1 = new Son("Li hfeng", 25);
                    instance1.colors.push("black");
                    instance1.sayAge();
                    instance1.sayName();

                    console.log("--------------------------------------------");
                })();
            }

            function case4() {
                (function () {
                    // 4. 原型继承
                    // 原始写法，有参数共享的问题
                    function object(o) {
                        function F() {}
                        F.prototype = o;
                        return new F();
                    }
                    // 目前用create规范化
                    var person = {
                        name: "Van",
                    };
                    var anotherPerson = Object.create(person, {
                        name: {
                            value: "Louis",
                        },
                    });

                    // person.name = "改过了"

                    console.log("case4");
                    console.log(anotherPerson);
                    console.log("--------------------------------------------");
                })();
            }

            function case5() {
                (function () {
                    function object(o) {
                        function F() {}
                        F.prototype = o;
                        return new F();
                    }
                    // 5. 寄生继承，因不能做到函数复用而效率较低，这一点与构造函数模式类似
                    function createAnother(original) {
                        var clone = object(original);
                        clone.sayHi = function () {
                            console.log("Hi! ", this.name);
                        };
                        return clone;
                    }
                    let person = {
                        name: "L HFeng",
                    };
                    let instance1 = createAnother(person);
                    instance1.sayHi();
                    console.log("--------------------------------------------");
                })();
            }

            function case6() {
                (function () {
                    function object(o) {
                        function F() {}
                        F.prototype = o;
                        return new F();
                    }
                    // 6. 寄生组合继承，为了解决组合继承调用两次父构造函数而出现
                    function extend(subClass, superClass) {
                        var _proto = object(superClass.prototype);
                        _proto.constructor = subClass;
                        subClass.prototype = _proto;
                    }

                    // 另一种更有效的写法
                    // function extend(subClass, superClass) {
                    //     var F = function () {};
                    //     F.prototype = superClass.prototype;
                    //     subClass.prototype = new F();
                    //     subClass.prototype.constructor = subClass;

                    //     subClass.superclass = superClass.prototype;
                    //     if (
                    //         superClass.prototype.constructor ==
                    //         Object.prototype.constructor
                    //     ) {
                    //         superClass.prototype.constructor = superClass;
                    //     }
                    // }

                    function Son() {
                        this.name = "L HFeng";
                    }

                    function Father() {}
                    Father.prototype.sayHi = function () {
                        console.log("my name is " + this.name);
                    };

                    extend(Son, Father);
                    let instance1 = new Son();
                    console.log("case6", instance1);
                    instance1.sayHi();
                    console.log("of father:", instance1 instanceof Father);
                    console.log("of son:", instance1 instanceof Son);
                    console.log("--------------------------------------------");
                })();
            }
        </script>
    </body>
</html>
